﻿using System;
using OverStore.Runtime.ObjectRepository.Accessors;

namespace OverStore.Configuration.ObjectRepository.Accessors
{
    /// <summary>
    /// Access object data or data assosiated with object using 
    /// specified delegates.
    /// </summary>
    public sealed class ExpressionAccessor<TInstance, TValue> : ITypedAccessor<TInstance, TValue>
        where TInstance : class
    {
        private readonly Func<TInstance, TValue> _GetValueFunction;

        private readonly Action<TInstance, TValue> _SetValueFunction;

        /// <summary>
        /// Initializes new instance of ExpressionAccessor class.
        /// </summary>
        /// <param name="getValue">Delegate that gets result of specified instance.</param>
        /// <param name="setValue">Delegate that sets result to specified instance.</param>
        /// <exception cref="ArgumentNullException"><paramref name="getValue"/> is null reference.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="setValue"/> is null reference.</exception>
        public ExpressionAccessor(Func<TInstance, TValue> getValue, Action<TInstance, TValue> setValue)
        {
            if (getValue == null)
                throw new ArgumentNullException("getValue");
            if (setValue == null)
                throw new ArgumentNullException("setValue");
            this._GetValueFunction = getValue;
            _SetValueFunction = setValue;
        }

        /// <summary>
        /// Gets the result.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <returns>Value provided by accessor.</returns>
        public TValue GetValue(TInstance instance)
        {
            return _GetValueFunction(instance);
        }

        /// <summary>
        /// Sets the result.
        /// </summary>
        /// <param name="instance">The instance to update result of it.</param>
        /// <param name="value">The result need to be applied to instance.</param>
        public void SetValue(TInstance instance, TValue value)
        {
            _SetValueFunction(instance, value);
        }

        /// <summary>
        /// Get result from specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <returns>Value provided by accessor.</returns>
        object IAccessor.GetValue(object instance)
        {
            return GetValue((TInstance)instance);
        }

        /// <summary>
        /// Set result to specified instance.
        /// </summary>
        /// <param name="instance">The instance to update result of it.</param>
        /// <param name="value">The result need to be applied to instance.</param>
        void IAccessor.SetValue(object instance, object value)
        {
            SetValue((TInstance)instance, (TValue)value);
        }
    }
}